<?php
/**
 * Events Calendar.
 * This calendar gives a possibility to handle events.
 *
 * @program   Calendar
 * @version   1.0
 * @author    Victor Nabatov <greenray.spb@gmail.com>
 * @copyright (c) 2016 Victor Nabatov
 * @license   Creative Commons Attribution-ShareAlike 4.0 International
 * @file      calendar.class.php
 * @package   Calendar
 * @overview  Creates calendar with ability to handle events:
 *            posted news, articles, sports events and so on.
 *            Requires PHP 5.4+
 */

class CALENDAR {

    /** @var array Events for specific date */
    private $events = [];

    /** @var array Calendar options */
    private $options = [];

    /** @var array Auxiliary variable */
    private $temp = [];

    /** @var array Current date */
    private $today = [];

    /**
     * Class initialization.
     *
     * @param array $options Callendar options
     */
    public function __construct($options) {
        $this->options = $options;
    }

    /**
     * Sets current date.
     *
     * @param integer $month    Month
     * @param integer $year     Year
     */
    public function set($month, $year) {
        $this->temp['first_day']      = mktime(0, 0, 0, $month, 1, $year);
        $this->temp['first_day_week'] = date('w', $this->temp['first_day']);
        $this->temp['num_of_days']    = date('t', $this->temp['first_day']);
    }

    /**
     * Creates calendar.
     *
     * @param  integer $yearCurrent   Current year
     * @param  integer $yearSelected  Selected year
     * @param  integer $monthSelected Selected month
     * @return array                  Data for calendar
     */
    function create($yearCurrent, $yearSelected, $monthSelected) {
        $months = [];
        foreach ([1 => 'January',
                  2 => 'February',
                  3 => 'March',
                  4 => 'April',
                  5 => 'May',
                  6 => 'June',
                  7 => 'July',
                  8 => 'August',
                  9 => 'September',
                 10 => 'October',
                 11 => 'November',
                 12 => 'December'] as $num => $month) {
            $months[$num]['name'] = $this->options['datetime'][$month];
            $months[$num]['num']  = $num;
            if ($num == $monthSelected) {
                $months[$num]['selected'] = TRUE;
            }
        }

        $years = [];
        for ($num = 2010; $num <= $yearCurrent; $num++) {
            $years[$num]['year'] = $num;
            if ($num == $yearSelected) {
                 $years[$num]['selected'] = TRUE;
            }
        }
        $position = ($this->temp['first_day_week'] == 0) ? 7 : $this->temp['first_day_week'];
        $calendar = [];
        $string   = 1;
        $showed   = 1;

        while ($showed <= $this->temp['num_of_days']) {
            if ($position > 1) {
                $calendar[$string]['span'] = $position - 1;
            }
            $inc = 0;
            for ($i = $showed; $i < ($showed + 7) && ($i <= $this->temp['num_of_days']) && ($position <= 7); ++$i) {
                #
                # Assign event(s) to a date
                #
                $class = 'event';
                if (empty($this->events[$i]))
                     $class = '';
                else $calendar[$string]['dates'][$i]['events'] = $this->events[$i];
                #
                # Show current date
                #
                if (!empty($this->today[$i]) && ($yearCurrent === $yearSelected)) {
                    $class .= ' special';
                }
                $calendar[$string]['dates'][$i]['class'] = $class;
                $calendar[$string]['dates'][$i]['date']  = $i;
                ++$position;
                ++$inc;
            }
            $showed   = $showed + $inc;
            $position = 0;
            ++$string;
        }
        return [
            'month'  => $this->localiseDate(date('F Y', $this->temp['first_day'])),
            'header' => $this->localiseDate(
                '<th>Mon</th>
                 <th>Tue</th>
                 <th>Wed</th>
                 <th>Thu</th>
                 <th>Fri</th>
                 <th>Sat</th>
                 <th>Sun</th>'
            ),
            'calendar' => $calendar,
            'months'   => $months,
            'years'    => $years
        ];
    }

    /**
     * Assigns event to the calendar.
     *
     * @param integer $day  Date
     * @param string  $item Link to existing post
     */
    function event($day, $item) {
        $day = intval($day);
        if (!empty($this->events[$day]))
             $this->events[$day] = $this->events[$day].', '.$item;
        else $this->events[$day] = $item;
    }

    /**
     * Highlights current date.
     *
     * @param integer $day   Date
     * @param string  $style Style for highlighting
     */
    function highlight($day, $style = '!') {
        $this->today[intval($day)] = $style;
    }

    /**
     * Formats date and time according to user's timezone.
     *
     * @param  string $format Format
     * @param  string $date	  Date
     * @return string         Formatted date/time
     */
    function formatTime($format, $date) {
        $translate = [];
        if ($this->options['locale'] !== 'en') {
            foreach($this->options['datetime'] as $match => $replace) {
                $translate[$match] = $replace;
            }
        }
        return empty($translate)
            ? gmdate($format, $date + (3600 * $this->options['timezone']))
            : strtr(gmdate($format, $date + (3600 * $this->options['timezone'])), $translate);
    }

    /**
     * Returns localised date from string generated by date().
     *
     * @param  string $string Date to localize
     * @return string         Localized date
     */
    function localiseDate($string) {
        $translate = [];
        if ($this->options['language'] !== 'english') {
            foreach($this->options['datetime'] as $match => $replace) {
                $translate[$match] = $replace;
            }
        }
        return empty($translate) ? $string : strtr($string, $translate);
    }
}
